package com.qdlc.p2p.biz.service.impl;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

import com.google.common.base.Joiner;
import com.google.common.primitives.Longs;
import com.qd.p2p.adapter.Response;
import com.qd.p2p.adapter.enums.ResponseStatus;
import com.qdlc.p2p.biz.processor.event.RedEnvelopePayNoticeEvent;
import com.qdlc.p2p.biz.processor.event.TppDispatchValueEvent;
import com.qdlc.p2p.biz.service.AbstractPayService;
import com.qdlc.p2p.biz.service.RPCRequestService;
import com.qdlc.p2p.common.constant.GeneralAccountType;
import com.qdlc.p2p.common.constant.Global;
import com.qdlc.p2p.common.constant.LogTplNid;
import com.qdlc.p2p.common.constant.NotifyUrl;
import com.qdlc.p2p.common.constant.RedEnvelopeType;
import com.qdlc.p2p.common.constant.RedStatus;
import com.qdlc.p2p.common.constant.SinaConstant;
import com.qdlc.p2p.common.constant.TradeCode;
import com.qdlc.p2p.common.constant.TradeStatus;
import com.qdlc.p2p.common.core.ReturnResult;
import com.qdlc.p2p.common.core.queue.QueueHandlers;
import com.qdlc.p2p.common.exception.BusinessException;
import com.qdlc.p2p.common.util.BeanCopierUtils;
import com.qdlc.p2p.common.util.DataUtils;
import com.qdlc.p2p.common.util.DateUtil;
import com.qdlc.p2p.dal.dao.TppDao;
import com.qdlc.p2p.dal.dao.UserDao;
import com.qdlc.p2p.dal.dao.UserRedEnvelopeDao;
import com.qdlc.p2p.dal.dto.Tpp;
import com.qdlc.p2p.dal.dto.TppTradeLog;
import com.qdlc.p2p.dal.dto.User;
import com.qdlc.p2p.dal.model.TppModel;
import com.qdlc.p2p.dal.model.TppTradeLogModel;

/**
 * 红包兑换 代付服务类
 *
 * @author wuaj
 * @version 1.1.0.0
 */
@Service("redEnvelopePayService")
public class RedEnvelopePayServiceImpl extends AbstractPayService {

    private final static String BUSINESS_NAME = "红包兑现代付-";

    /**
     * 代收交易Log
     */
    private ThreadLocal<TppTradeLog> collectTppTradeLogLocal = new ThreadLocal<TppTradeLog>();

    @Resource
    private UserRedEnvelopeDao userRedEnvelopeDao;

    /**
     * 重新调度
     */
    private ThreadLocal<Integer> autoDispatch = new ThreadLocal<Integer>();

    @Resource
    @Qualifier("bfRPCRequestService")
    private RPCRequestService<Response, TppTradeLog> rpcRequestService;

    @Resource
    private TppDao tppDao;

    @Resource
    private UserDao userDao;

    @Override
    public String getCallBackUrl() {
        return WEB_URL.concat(NotifyUrl.RED_ENVELOPE_NOTIFY_PAY_URL).concat(".html");
    }

    @Override
    public String getBusinessName() {
        return BUSINESS_NAME;
    }

    @Override
    public void analyzePars(Object... pars) {
        this.autoDispatch.set(0);// 默认非自动
        if (pars.length != 1 && pars.length != 2) {
            throw new IllegalArgumentException("参数个数正确!");
        }
        try {
            TppTradeLog collectTppTradeLog = (TppTradeLog) pars[0]; // 代收交易Log
            if (collectTppTradeLog == null) {
                throw new IllegalArgumentException("参数类型正确!");
            }
            collectTppTradeLogLocal.set(collectTppTradeLog);
            if (pars.length == 2) {
                this.autoDispatch.set((Integer) pars[1]);// 自动调度标识 1:自动,0:非自动
            }
        } catch (Exception ex) {
            throw new IllegalArgumentException("参数类型正确!");
        }
    }

    @Override
    public TppTradeLog getTppTradeLog() {
        TppTradeLog tppTradeLog = new TppTradeLog();
        BeanCopierUtils.copyProperties(collectTppTradeLogLocal.get(), tppTradeLog);
        tppTradeLog.setId(0l);
        // 非自动调度
        if (autoDispatch.get() != 1) {
            tppTradeLog.setAssociateOrderId(tppTradeLog.getOrderNo());  // 代收订单
            tppTradeLog.setTradeType(TradeCode.C2003.toString());
        }
        tppTradeLog.setAutoDispatch(autoDispatch.get());// 自动调度标识 1:自动,0:非自动
        return tppTradeLog;
    }

    @Override
    public ReturnResult requestOrder(TppTradeLog tppTradeLog) {
        Response response = rpcRequestService.redExchangePayRequest(tppTradeLog);
        // 未知状态通知 交由消息队列处理
        if (response == null || response.getStatus() == ResponseStatus.UN_KNOW) {
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_UN_KNOW);
            return new ReturnResult(true, "系统处理中!请查看兑现结果!");
        }
        // 代付调用成功
        if (response.getStatus() == ResponseStatus.SUCCESS) {
            tppTradeLog.setRemark(getBusinessName().concat("提交成功"));
            tppTradeService.update(tppTradeLog);
            // 成功通知
            notice(tppTradeLog, TradeStatus.TRADE_STATUS_FINISHED);
            requestOrderSuccess(tppTradeLog); // 请求成功处理
        } else {
            String message = response == null ? "RPC调用失败" : MessageFormat.format("代付失败! resCode:{0} resMessage:{1}",
                    response.getResponseCode(),
                    response.getResponseMsg());
            String responseMessage = response == null ? "RPC调用失败" : response.getResponseMsg();
            logger.info(message);

            // 置为废单
            tppTradeLog.setTradeStatus(TradeStatus.TRADE_STATUS_INVALID.getStatus());
            tppTradeLog.setRemark(getBusinessName().concat("提交失败-" + responseMessage));
            tppTradeService.update(tppTradeLog);
            requestOrderFail(tppTradeLog); // 请求失败处理
            return new ReturnResult(true, message);
        }
        return ReturnResult.SUCCESS;
    }

    @Override
    public void requestOrderFail(TppTradeLog tppTradeLog) {
        Map<String, Object> memoMap = DataUtils.getMemo(tppTradeLog.getMemo());
        long[] redIds = Longs.toArray(((ArrayList) memoMap.get("redIds")));
        byte redType = DataUtils.toByte(memoMap.get("redType")); // 红包类型
        long userId = DataUtils.toLong(memoMap.get("userId"));
        List<Long> ids = getRedIdList(redIds);

        // 保存调度任务信息
        Tpp tpp = new Tpp();
        tpp.setStatus(TppModel.STATUS_FAIL);
        tpp.setRespDesc(tppTradeLog.getRemark());
        User toUser = userDao.getUserById(userId);
        tpp.setServiceType(TppModel.RED_ENVELOPE_EXCHANGE);
        tpp.setTppType(SinaConstant.TRADE_SERVICE_RED_ENVELOPE_EXCHAGE);
        tpp.setTppUserCustId(tppTradeLog.getTppUserCustId());
        tpp.setToUser(toUser);
        tpp.setToTppUserCustId(tppTradeLog.getToTppUserCustId());
        tpp.setMoney(tppTradeLog.getAmount());
        tpp.setOrderId(tppTradeLog.getOrderNo());
        tpp.setOrderDate(DateUtil.dateStr7(tppTradeLog.getAddTime()));
        tpp.setExtended(Joiner.on(",").skipNulls().join(ids));
        tpp.setTradeType(tppTradeLog.getTradeType());
        tpp.setFlag(redType + "");
        tpp.setAddTime(DateUtil.getNow());
        tppDao.save(tpp);

        // 调度任务交由队列处理(延时处理)
        if (tppTradeLog.getAutoDispatch() != TppTradeLogModel.IS_AUTO) {// 防止重复处理
            TppModel tppModel = TppModel.instance(tpp);
            TppDispatchValueEvent tppDispatchValueEvent = new TppDispatchValueEvent();
            tppDispatchValueEvent.setTppModel(tppModel);
            QueueHandlers.put(tppDispatchValueEvent);
        }
    }

    @Override
    public void tradeFinish(TppTradeLog tppTradeLog, String depositStatus, double tradeAmount) {
        super.tradeFinishForStatus(tppTradeLog, depositStatus, tradeAmount);

        // 红包状态使用
        Map<String, Object> memoMap = DataUtils.getMemo(tppTradeLog.getMemo());

        // 红包类型
        byte redType = DataUtils.toByte(memoMap.get("redType"));
        // String redTypeStr = redType == RedEnvelopeType.CASH ? "现金红包" : "虚拟红包";

        // 红包置为使用
        long[] redEnvelopeIds = Longs.toArray(((ArrayList) memoMap.get("redIds"))); // 红包ID
        String useType = (String) memoMap.get("useType"); // 红包用途
        Long keyId = DataUtils.toLong(memoMap.get("keyId")); // 关联Key
        redUse(redEnvelopeIds, useType, keyId);

        // 资金变更
        long userId = DataUtils.toLong(memoMap.get("userId")); // 兑换的用户ID

        // 兑现 资金变更
        Global.setTransfer(LogTplNid.LOG_TPL_KEY, redType == RedEnvelopeType.CASH ?
                LogTplNid.CASH_RED_ENVELOPE : LogTplNid.VIRTUAL_RED_ENVELOPE);
        Global.setTransfer("money", tradeAmount);
        accountService.addUseMoneyAndTotal(userId, GeneralAccountType.PLATFORM, tradeAmount);
    }

    @Override
    public void tradeSuccess(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeSuccessForStatus(tppTradeLog, depositStatus);
    }

    @Override
    public void tradeFail(TppTradeLog tppTradeLog, String depositStatus) {
        super.tradeFailForStatus(tppTradeLog, depositStatus);
        // 回调失败
        requestOrderFail(tppTradeLog);
    }

    /**
     * 红包使用
     *
     * @param redIds  红包IDS
     * @param useType 用途
     * @param keyId   相关Key
     */
    private void redUse(long[] redIds, String useType, Long keyId) {
        List<Long> ids = getRedIdList(redIds);
        if (userRedEnvelopeDao.doUse(ids, useType, RedStatus.FREEZE, keyId) != ids.size()) {
            logger.error("使用红包出错,请检查红包状态!redIds=" + Joiner.on(",").skipNulls().join(ids));
            throw new BusinessException("使用红包出错,请检查红包状态!redIds=" + Joiner.on(",").skipNulls().join(ids));
        }
    }

    /**
     * 把红包的long数据转成红包list
     *
     * @param redIds 红包IDS
     */
    private List<Long> getRedIdList(long[] redIds) {
        List<Long> ids = new ArrayList<Long>(redIds.length);
        for (long redId : redIds) {
            ids.add(redId);
        }
        return ids;
    }

    /**
     * 发送投标通知 交由队列来执行
     */
    private void notice(TppTradeLog tppTradeLog, TradeStatus tradeStatus) {
        RedEnvelopePayNoticeEvent event = new RedEnvelopePayNoticeEvent();
        event.setClientIp(Global.getIP());
        event.setTradeStatus(tradeStatus);
        event.setTppTradeLog(tppTradeLog);
        event.setOutOrderNo(tppTradeLog.getOrderNo());
        event.setDepositAmount(tppTradeLog.getMoney());
        QueueHandlers.put(event);
    }
}
